#include "cursor_wrapper.h"
#include "wlroots-full.hpp"

#include "log.h"

CursorWrapper::CursorWrapper(Server *server)
    : m_server(server)
{
    /*
     * Creates a cursor, which is a wlroots utility for tracking the cursor
     * image shown on screen.
     */
    m_cursor = wlr_cursor_create();
    wlr_cursor_attach_output_layout(m_cursor, m_server->m_output_layout);

    /* Creates an xcursor manager, another wlroots utility which loads up
     * Xcursor themes to source cursor images from and makes sure that cursor
     * images are available at all scale factors on the screen (necessary for
     * HiDPI support). */
    m_cursor_mgr = wlr_xcursor_manager_create(NULL, 24);

    /*
     * wlr_cursor *only* displays an image on screen. It does not move around
     * when the pointer moves. However, we can attach input devices to it, and
     * it will generate aggregate events for all of them. In these events, we
     * can choose how we want to process them, forwarding them to clients and
     * moving the cursor around. More detail on this process is described in
     * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html.
     *
     * And more comments are sprinkled throughout the notify functions above.
     */
    m_cursor_mode = BWL_CURSOR_PASSTHROUGH;

    m_cursor_motion.connect(&m_cursor->events.motion, [=](void* data) {
        wlr_pointer_motion_event* event = (wlr_pointer_motion_event*)data;
        cursor_motion(event);
    });

    m_cursor_motion_absolute.connect(&m_cursor->events.motion_absolute, [=](void* data) {
        wlr_pointer_motion_absolute_event* event = (wlr_pointer_motion_absolute_event*)data;
        cursor_motion_absolute(event);
    });

    m_cursor_button.connect(&m_cursor->events.button, [=](void *data) {
        wlr_pointer_button_event* event = (wlr_pointer_button_event*)data;
        cursor_button(event);
    });

    m_cursor_axis.connect(&m_cursor->events.axis, [=](void* data) {
        wlr_pointer_axis_event* event = (wlr_pointer_axis_event*)data;
        cursor_axis(event);
    });

    m_cursor_frame.connect(&m_cursor->events.frame, [=](void* data) {
        cursor_frame();
    });
}

CursorWrapper::~CursorWrapper()
{
}

void CursorWrapper::set_cursor_default()
{
    wlr_cursor_set_xcursor(m_cursor, m_cursor_mgr, "default");
}

void CursorWrapper::cursor_motion(wlr_pointer_motion_event *event)
{
    wlr_cursor_move(m_cursor, &event->pointer->base,
                    event->delta_x, event->delta_y);
    process_cursor_motion(event->time_msec);
}

void CursorWrapper::cursor_motion_absolute(wlr_pointer_motion_absolute_event *event)
{
    wlr_cursor_warp_absolute(m_cursor, &event->pointer->base, event->x,
                             event->y);
    process_cursor_motion(event->time_msec);
}

void CursorWrapper::cursor_button(wlr_pointer_button_event *event)
{
    sig_cursor_button.emit(m_cursor->x, m_cursor->y, event);
}

void CursorWrapper::cursor_axis(wlr_pointer_axis_event *event)
{
    sig_cursor_axis.emit(event);
}

void CursorWrapper::cursor_frame()
{
    sig_cursor_frame.emit();
}

void CursorWrapper::process_cursor_motion(uint32_t time)
{
    wlr_log(WLR_DEBUG, "cursor motion, x:%lf, y:%lf, time:%u", m_cursor->x, m_cursor->y, time);
    /* If the mode is non-passthrough, delegate to those functions. */
    if (m_cursor_mode == BWL_CURSOR_MOVE) {
        process_cursor_move(time);
        return;
    } else if (m_cursor_mode == BWL_CURSOR_RESIZE) {
        process_cursor_resize(time);
        return;
    }

    sig_cursor_motion.emit(m_cursor->x, m_cursor->y, time);
}

void CursorWrapper::process_cursor_move(uint32_t time)
{
    sig_cursor_move.emit(m_cursor->x - m_grab_x, m_cursor->y - m_grab_y);
}

void CursorWrapper::process_cursor_resize(uint32_t time)
{
    sig_cursor_resize.emit(time);
}
